eletrotupi / tcc/ frontend/app/triggers/[id].tsxce66d00c4b0f0549e45ca233432cd6098e28455c
6.3 KB | 244 lines Raw
1
import { useState, useEffect, use } from "react";
2
import {
3
  StyleSheet,
4
  Text,
5
  View,
6
  ScrollView,
7
  Pressable,
8
  ActivityIndicator,
9
} from "react-native";
10
import { useLocalSearchParams, router, Stack } from "expo-router";
11
import { format } from "date-fns";
12
import { ptBR } from "date-fns/locale";
13

14
import { Card, SubtleInfoCard } from "@/components/ui/Cards";
15
import { Badge } from "@/components/ui/Badge";
16
import { Button } from "@/components/ui/Button";
17
import { Col, Between, Row } from "@/components/ui/LayoutHelpers";
18
import { Section, SectionHeader } from "@/components/ui/Sections";
19
import { Theme, Typography, Colors, Spacing } from "@/constants/theme";
20
import { MaterialIcons } from "@expo/vector-icons";
21
import { getTriggerCategory } from "@/constants/trigger-categories";
22
import {
23
  useTrigger,
24
  useDeleteTrigger,
25
} from "@/hooks/useTrigger.queries";
26
import { formatMoment } from "@/lib/utils/time";
27

28
export default function TriggerDetailsScreen() {
29
  const { id } = useLocalSearchParams<{ id: string }>();
30
  const { data: trigger, isLoading, isError } = useTrigger(id);
31
  const deleteTrigger = useDeleteTrigger();
32
  const [minutesLeft, setMinutesLeft] = useState(0);
33
  const [canDelete, setCanDelete] = useState(false);
34

35
  useEffect(() => {
36
    const updateTimer = () => {
37
      const createdAt = new Date(trigger?.createdAt);
38
      if (!createdAt) return;
39

40
      const msSinceCreation = Date.now() - createdAt.getTime();
41
      const deletionWindowMs = 5 * 60 * 1000;
42

43
      const newCanDelete = msSinceCreation < deletionWindowMs;
44
      const newMinutesLeft = Math.max(
45
        0,
46
        Math.ceil((deletionWindowMs - msSinceCreation) / 60000),
47
      );
48

49
      setCanDelete(newCanDelete);
50
      setMinutesLeft(newMinutesLeft);
51
    };
52

53
    if (isLoading) return;
54

55
    updateTimer(); // Set initial values
56

57
    const interval = setInterval(updateTimer, 1000);
58

59
    return () => clearInterval(interval);
60
  }, [trigger, isLoading]);
61

62
  if (isLoading) {
63
    return (
64
      <View style={styles.centered}>
65
        <ActivityIndicator size="large" color={Colors.light.tint} />
66
      </View>
67
    );
68
  }
69

70
  if (isError || !trigger) {
71
    return (
72
      <View style={styles.centered}>
73
        <Text style={styles.errorText}>Registro não encontrado.</Text>
74
        <Pressable onPress={() => router.back()} style={styles.backButton}>
75
          <Text style={styles.backButtonText}>Voltar</Text>
76
        </Pressable>
77
      </View>
78
    );
79
  }
80

81
  const meta = getTriggerCategory(trigger.category);
82

83
  const handleDelete = async () => {
84
    await deleteTrigger.mutateAsync(String(trigger.id));
85

86
    router.back();
87
  };
88

89
  const deleteButtonMsg = () => {
90
    const msg = "Excluir Registro";
91
    const timeLeftStr = `(Disponível por ${minutesLeft < 1 ? "<1" : minutesLeft}:00)`;
92
    const suffix = canDelete ? timeLeftStr : "(Expirado)";
93

94
    return `${msg} ${suffix}`;
95
  };
96

97
  return (
98
    <>
99
      <Stack.Screen options={{ title: "Ver detalhes" }} />
100

101
      <ScrollView
102
        style={styles.container}
103
        contentContainerStyle={styles.content}
104
        showsVerticalScrollIndicator={false}
105
      >
106
        {/* Header card */}
107
        <Card style={styles.headerCard}>
108
          <Row style={{ alignItems: "center" }}>
109
            <View style={[styles.iconWrap, { backgroundColor: meta.iconBackground }]}>
110
              <MaterialIcons name={meta.icon} size={22} color={meta.color} />
111
            </View>
112

113
            <Col gap={2}>
114
              <Text style={styles.triggerLabel}>
115
                {meta.label}
116
              </Text>
117
              <Text style={styles.triggerMoment}>
118
                {formatMoment(new Date(trigger.moment))}
119
              </Text>
120
            </Col>
121
          </Row>
122
        </Card>
123

124
        {/* Annotation / notes */}
125
        {trigger.comment && (
126
          <Section>
127
            <SectionHeader title="Anotações" variant="subtle" />
128
            <Card style={{ padding: Spacing.cardGap }}>
129
              <Text style={styles.annotation}>
130
                "{trigger.comment?.trim()}"
131
              </Text>
132
            </Card>
133
          </Section>
134
        )}
135

136
        {/* Delete */}
137
        <View style={styles.section}>
138
          <Text style={styles.sectionLabel}>GESTÃO DO REGISTRO</Text>
139
          <Button
140
            variant="outline"
141
            isLoading={false}
142
            disabled={!canDelete}
143
            onPress={() => router.push(`/triggers/edit/${trigger.id}`)}
144
            title="Editar Registro"
145
          />
146
          <Button
147
            variant="danger"
148
            isLoading={deleteTrigger.isPending}
149
            disabled={!canDelete || deleteTrigger.isPending}
150
            onPress={handleDelete}
151
            title={deleteButtonMsg()}
152
          />
153

154
          <SubtleInfoCard
155
            style={styles.deleteInfo}
156
            text={`
157
              A exclusão de registros é permitida apenas nos primeiros 5 minutos
158
              após o envio para garantir a integridade do histórico emocional.
159
              `}
160
          />
161
        </View>
162
      </ScrollView>
163
    </>
164
  );
165
}
166

167
const styles = StyleSheet.create({
168
  container: {
169
    flex: 1,
170
    backgroundColor: Theme.colors.background,
171
  },
172
  content: {
173
    padding: Spacing.containerPadding,
174
    gap: Spacing.sectionGap,
175
    paddingBottom: 48,
176
  },
177
  iconWrap: {
178
    width: 40,
179
    height: 40,
180
    borderRadius: 20,
181
    alignItems: 'center',
182
    justifyContent: 'center',
183
    marginTop: 1
184
  },
185
  headerCardContent: {
186
    flexDirection: "row",
187
    alignItems: "center",
188
    gap: 8,
189
  },
190
  triggerLabel: {
191
    ...Typography.headlineLg,
192
    color: Colors.light.text,
193
  },
194
  centered: {
195
    flex: 1,
196
    justifyContent: "center",
197
    alignItems: "center",
198
    gap: 12,
199
  },
200
  errorText: {
201
    fontSize: 15,
202
    color: Colors.light.textSecondary,
203
  },
204
  backButton: {
205
    paddingHorizontal: 20,
206
    paddingVertical: 10,
207
    backgroundColor: Theme.colors.tint,
208
    borderRadius: Theme.borderRadius.md,
209
  },
210
  backButtonText: {
211
    fontWeight: "700",
212
    color: Theme.colors.text,
213
  },
214

215
  // Header
216
  headerCard: {
217
    padding: 16,
218
  },
219
  triggerMoment: {
220
    fontSize: 13,
221
    color: Colors.light.textSecondary,
222
  },
223

224
  // Sections
225
  section: {
226
    gap: 8,
227
  },
228
  sectionLabel: {
229
    fontSize: 11,
230
    fontWeight: "600",
231
    letterSpacing: 0.8,
232
    color: Colors.light.textSecondary,
233
    textTransform: "uppercase",
234
  },
235

236
  // Annotation
237
  annotation: {
238
    fontSize: 14,
239
    lineHeight: 22,
240
    color: Colors.light.textSecondary,
241
    fontStyle: "italic",
242
    padding: 4,
243
  },
244
});